{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#!/usr/bin/env python\n",
    "# -*- coding: utf-8 -*-\n",
    "\n",
    "__author__ = 'Michael Liao'\n",
    "\n",
    "import os, re, time, base64, hashlib, logging\n",
    "\n",
    "from transwarp.web import get, post, ctx, view, interceptor, HttpError\n",
    "\n",
    "from apis import api, APIError, APIValueError, APIPermissionError, APIResourceNotFoundError\n",
    "\n",
    "from models import User, Blog, Comment\n",
    "from config import configs\n",
    "\n",
    "_COOKIE_NAME = 'awesession'\n",
    "_COOKIE_KEY = configs.session.secret\n",
    "\n",
    "def make_signed_cookie(id, password, max_age):\n",
    "    # build cookie string by: id-expires-md5\n",
    "    expires = str(int(time.time() + (max_age or 86400)))\n",
    "    L = [id, expires, hashlib.md5('%s-%s-%s-%s' % (id, password, expires, _COOKIE_KEY)).hexdigest()]\n",
    "    return '-'.join(L)\n",
    "\n",
    "def parse_signed_cookie(cookie_str):\n",
    "    try:\n",
    "        L = cookie_str.split('-')\n",
    "        if len(L) != 3:\n",
    "            return None\n",
    "        id, expires, md5 = L\n",
    "        if int(expires) < time.time():\n",
    "            return None\n",
    "        user = User.get(id)\n",
    "        if user is None:\n",
    "            return None\n",
    "        if md5 != hashlib.md5('%s-%s-%s-%s' % (id, user.password, expires, _COOKIE_KEY)).hexdigest():\n",
    "            return None\n",
    "        return user\n",
    "    except:\n",
    "        return None\n",
    "\n",
    "def check_admin():\n",
    "    user = ctx.request.user\n",
    "    if user and user.admin:\n",
    "        return\n",
    "    raise APIPermissionError('No permission.')\n",
    "\n",
    "@interceptor('/')\n",
    "def user_interceptor(next):\n",
    "    logging.info('try to bind user from session cookie...')\n",
    "    user = None\n",
    "    cookie = ctx.request.cookies.get(_COOKIE_NAME)\n",
    "    if cookie:\n",
    "        logging.info('parse session cookie...')\n",
    "        user = parse_signed_cookie(cookie)\n",
    "        if user:\n",
    "            logging.info('bind user <%s> to session...' % user.email)\n",
    "    ctx.request.user = user\n",
    "    return next()\n",
    "\n",
    "@interceptor('/manage/')\n",
    "def manage_interceptor(next):\n",
    "    user = ctx.request.user\n",
    "    if user and user.admin:\n",
    "        return next()\n",
    "    raise HttpError.seeother('/signin')\n",
    "\n",
    "@view('blogs.html')\n",
    "@get('/')\n",
    "def index():\n",
    "    blogs = Blog.find_all()\n",
    "    return dict(blogs=blogs, user=ctx.request.user)\n",
    "\n",
    "@view('signin.html')\n",
    "@get('/signin')\n",
    "def signin():\n",
    "    return dict()\n",
    "\n",
    "@get('/signout')\n",
    "def signout():\n",
    "    ctx.response.delete_cookie(_COOKIE_NAME)\n",
    "    raise HttpError.seeother('/')\n",
    "\n",
    "@api\n",
    "@post('/api/authenticate')\n",
    "def authenticate():\n",
    "    i = ctx.request.input(remember='')\n",
    "    email = i.email.strip().lower()\n",
    "    password = i.password\n",
    "    remember = i.remember\n",
    "    user = User.find_first('where email=?', email)\n",
    "    if user is None:\n",
    "        raise APIError('auth:failed', 'email', 'Invalid email.')\n",
    "    elif user.password != password:\n",
    "        raise APIError('auth:failed', 'password', 'Invalid password.')\n",
    "    # make session cookie:\n",
    "    max_age = 604800 if remember=='true' else None\n",
    "    cookie = make_signed_cookie(user.id, user.password, max_age)\n",
    "    ctx.response.set_cookie(_COOKIE_NAME, cookie, max_age=max_age)\n",
    "    user.password = '******'\n",
    "    return user\n",
    "\n",
    "_RE_EMAIL = re.compile(r'^[a-z0-9\\.\\-\\_]+\\@[a-z0-9\\-\\_]+(\\.[a-z0-9\\-\\_]+){1,4}$')\n",
    "_RE_MD5 = re.compile(r'^[0-9a-f]{32}$')\n",
    "\n",
    "@api\n",
    "@post('/api/users')\n",
    "def register_user():\n",
    "    i = ctx.request.input(name='', email='', password='')\n",
    "    name = i.name.strip()\n",
    "    email = i.email.strip().lower()\n",
    "    password = i.password\n",
    "    if not name:\n",
    "        raise APIValueError('name')\n",
    "    if not email or not _RE_EMAIL.match(email):\n",
    "        raise APIValueError('email')\n",
    "    if not password or not _RE_MD5.match(password):\n",
    "        raise APIValueError('password')\n",
    "    user = User.find_first('where email=?', email)\n",
    "    if user:\n",
    "        raise APIError('register:failed', 'email', 'Email is already in use.')\n",
    "    user = User(name=name, email=email, password=password, image='http://www.gravatar.com/avatar/%s?d=mm&s=120' % hashlib.md5(email).hexdigest())\n",
    "    user.insert()\n",
    "    # make session cookie:\n",
    "    cookie = make_signed_cookie(user.id, user.password, None)\n",
    "    ctx.response.set_cookie(_COOKIE_NAME, cookie)\n",
    "    return user\n",
    "\n",
    "@view('register.html')\n",
    "@get('/register')\n",
    "def register():\n",
    "    return dict()\n",
    "\n",
    "@api\n",
    "@get('/api/users')\n",
    "def api_get_users():\n",
    "    users = User.find_by('order by created_at desc')\n",
    "    for u in users:\n",
    "        u.password = '******'\n",
    "    return dict(users=users)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
